<?php
$address = '192.168.0.3'; //Address to listen to
$port = 30175;            //Port to listen

$server_version = "Youloc.net Franson server 1.0";

require ("config.php"); //You can point this to the youloc config.php if you want.

set_time_limit(0);

error_reporting(E_ALL);

//For password
function invertString($str) {
	for ($i=strlen($str) - 1; $i >= 0; $i--) {
		$char = substr($str, $i, 1);
		if (preg_match("/[0-9]/", $char) > 0) {
			@	$newStr = $newStr . chr(9-(ord($char) - ord("0")) + ord("0"));
		}
		else if (preg_match("/[a-z]/", $char) > 0) {
			@	$newStr = $newStr . chr((ord("z") - ord("a")) - (ord($char) - ord("a")) + ord("A"));
		}
		else if (preg_match("/[A-Z]/", $char) > 0) {
			@	$newStr = $newStr . chr((ord("Z") - ord("A")) - (ord($char) - ord("A")) + ord("a"));
		}
	}
	return $newStr;
}

function get_NMEA_checksum($sentence) {
	// trim beginning of $sentance
	if (strpos($sentence, "$") !== false) {
		$sentence = substr($sentence, strpos($sentence, "$") + 1);
	}
	// trim end of $sentance
	if (strpos($sentence, "*") !== false) {
		$sentence = substr($sentence, 0, strpos($sentence, "*"));
	}

	// loop through characters to get a checksum
	$checksum = false;
	if (strlen($sentence) > 0) {
		$sentance_array = str_split($sentence);
		foreach($sentance_array as $key=>$letter) {
			// Is this the first value for the checksum?
			If ($checksum === false) {
				// Yes. Set the checksum to the value
				$checksum = $letter;
			} Else {
				// No. XOR the checksum with this character's value
				$checksum = $checksum ^ $letter;
			}
		}
		// return checksum as hexdecimal string
		return sprintf('%02X', ord($checksum));
	}


	// return boolean FALSE if no characters to evaluate
	return false;
}

function array_search_in_level($needle, $haystack, $key, &$result, $searchlevel = 0) {
	while(is_array($haystack) && isset($haystack[key($haystack)])) {
		if($searchlevel == 0 && key($haystack) == $key && $haystack[$key] == $needle) {
			$result = $haystack;
		} elseif($searchlevel > 0) {
			array_search_in_level($needle, $haystack[key($haystack)], $key, $result, $searchlevel - 1);
		}
		next($haystack);
	}
}

require_once 'Net/Server.php';
require_once 'Net/Server/Handler.php';

class Net_Server_Handler_Talkback extends Net_Server_Handler {
	/**
	* @access   public
	* @param    integer $clientId (ALWAYS 0!)
	* @param    string  $data
	*/
	function    onReceiveData( $clientId = 0, $data = "" ) {
		global $session_information, $server_version;
		$temp = explode('$', trim($data));
		$buffer = array();
		for ($i = 0; $i < count($temp); $i++) {
			if ($temp[$i] != "") {
				$buffer[] = '$'.$temp[$i];
			}
		}

		$temp = $this->_server->getClientInfo();
		$session_id = $temp["host"].$temp["port"].$temp["connectOn"];

		for ($b = 0; $b < count($buffer); $b++) {
			$data = $buffer[$b];
			$data = trim($data);
			if ($data == "") {
				continue;
			}
			$temp = explode("*",$data);
			$checksum = $temp[1];
			$message = explode(",", $temp[0]);
			if (get_NMEA_checksum($temp[0]) != $checksum) {
				echo "Checksum FAILED!\n";
			}
			unset($temp);
			switch($message[0]) {
				case '$FRLIN':
				if ($message[1] == "IMEI") {
					echo "IMEI login not yet implemented!\n";
					echo "IMEI recieved: $message[2] \n";
				} elseif ($message[1] == "") {
					
					//TODO: Login check should be implemented here.
					$username = $message[2];
					$password = invertString($message[3]);
					echo "Recieved info: Username: $username Password: $password \n";
				}
				
				array_search_in_level($username, $session_information, "username", &$result, 1);
				$return = 0;
				if (is_null($result)) {
					$session_information[]['username'] = $username;
					$session_information[count($session_information)-1]['session_id'] = count($session_information);
					$session_information[count($session_information)-1]['unique_id'] = $session_id;
					$session_information[count($session_information)-1]['data_type'] = "";
					$return = count($session_information);
				} else {
					for ($i = 0; $i < count($session_information); $i++) {
						if ($session_information[$i]['username'] == $username) {
							$session_information[$i]['unique_id'] = $session_id;
							$session_information[$i]['session_id'] = $i+1;
							$return = $i+1;
						}
					}
				}
				$send = '$FRSES,'.($return).'*';
				$send .= get_NMEA_checksum($send);

				$this->_server->sendData( $clientId,$send."\r\n");
				break;

				case '$FRVER':
				if ($message[1] == "1" AND $message[2] == "1") {
					echo "Client supports protocol version 1.1 and the client is: $message[3]\n";
				} else {
					echo "Oops, we don't support this protocol version yet: $message[1].$message[2] \n";
				}

				$send = '$FRVER,1,1,'.$server_version.'*';
				$send .= get_NMEA_checksum($send);

				$this->_server->sendData( $clientId,$send."\r\n");
				break;

				case '$FRWDT':
				for ($i = 0; $i < count($session_information); $i++) {
					if ($session_information[$i]['unique_id'] == $session_id AND isset($message[1])) {
						$session_information[$i]['data_type'] = $message[1];
					}
				}
				array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
				$username = $result["username"];
				$send = '$FRRET,'.$username.',_SaveTrackData,Inline*';
				$send .= get_NMEA_checksum($send);

				$this->_server->sendData( $clientId,$send."\r\n");

				break;

				case '$FRCMD':
				switch($message[2]) {
					case "_CreateTrackRecorder":
					array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
					$username = $result["username"];
					$id = $result["session_id"];
					$send = '$FRRET,'.$username.',_CreateTrackRecorder,Inline,'.$id.'*';
					$send .= get_NMEA_checksum($send);

					$this->_server->sendData( $clientId,$send."\r\n");
					break;

					case "_SendPosition":
					array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
					$username = $result["username"];
					$send = '$FRRET,'.$username.',_SendPosition,Inline*';
					$send .= get_NMEA_checksum($send);

					$this->_server->sendData( $clientId,$send."\r\n");
					break;
					
					case "_SaveTrackData":
					array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
					$username = $result["username"];
					$send = '$FRRET,'.$username.',_SaveTrackData,Inline*';
					$send .= get_NMEA_checksum($send);

					$this->_server->sendData( $clientId,$send."\r\n");
					break;

					case "_getupdaterules":
					echo "Sending update rules to client.\n";
					array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
					$username = $result["username"];
					$send = '$FRRET,'.$username.',_getupdaterules,Nmea,2*';
					$send .= get_NMEA_checksum($send);
					$this->_server->sendData( $clientId,$send."\r\n");
					$send = '$FRVAL,DistanceFilter,100.0*';
					$send .= get_NMEA_checksum('$FRVAL,DistanceFilter,100.0*');
					$send .= "\r\n";
					$send .= '$FRVAL,TimeFilter,30.0*';
					$send .= get_NMEA_checksum('$FRVAL,TimeFilter,30.0*');
					
					//Update rules: 100 Meters or 30 seconds whichever comes first.
					$this->_server->sendData( $clientId,$send."\r\n");

					break;

					default:
					$send = '$FRERR,NotSupported,'.$message[2].' not supported*';
					$send .= get_NMEA_checksum($send);

					$this->_server->sendData( $clientId,$send."\r\n");
					break;
				}

				break;
				case '$FRPOS':
				array_search_in_level($session_id, $session_information, "unique_id", &$result, 1);
				$username = $result["username"];
				$output = array();
				$output["user"] = $username;

				//Decimal degrees = whole number of degrees, plus minutes divided by 60, plus seconds divided by 3600;
				if ($message[4] == "W") {
					$output["latitude"] = "-".(substr($message[3], 0, 3)+(substr($message[3], 3)/60));
				} else {
					$output["latitude"] = (substr($message[3], 0, 3)+(substr($message[3], 3)/60));
				}

				if ($message[2] == "S") {
					$output["longitude"] = "-".(substr($message[1], 0, 2)+(substr($message[1], 2)/60));
				} else {
					$output["longitude"] = (substr($message[1], 0, 2)+(substr($message[1], 2)/60));
				}

				//echo "Long: $message[1].$message[2] Lat: $message[3].$message[4] \n";
				$output["altitude"] = $message[5];
				$output["speed"] = $message[6];
				$output["heading"]= $message[7];
				$output["time"] = $message[8]." ".$message[9];
				
				//TODO: This should be replaced with mysql queries.
				$url = "http://proto.youloc.net/logger.php?userkey=".$output["user"]."&lat=".$output["latitude"]."&lon=".$output["longitude"]."&alt=".$output["altitude"]."&speed=".$output["speed"]."&course=".$output["heading"];

				//This is just to limit time spent here. Otherwise the TCP/IP connection to the client times out when the client send backlog to server.
				if($b >= (count($buffer)-2)) {
					echo "URL: $url\n";
					file_get_contents($url);
				}
				break;

				break;
				default:
				echo "I don't know what to do with this: ";
				echo time()." $socket wrote: $data \n";
				break;
			}
		}
	}

	function		onClose($clientId = 0) {
		$temp = $this->_server->getClientInfo();
		$session_id = $temp["host"].$temp["port"].$temp["connectOn"];
		echo "Closing session - IP:".$temp['host']." Port:".$temp['port']." Connection time:".$temp['connectOn']." \n";
	}
	function		onConnect($clientId = 0) {
		$temp = $this->_server->getClientInfo();
		$session_id = $temp["host"].$temp["port"].$temp["connectOn"];
		echo "Opening session - IP:".$temp['host']." Port:".$temp['port']." Connection time:".$temp['connectOn']." \n";
	}
}

$session_information = array();
$clients = array();

// create a server that forks new processes
$server  = &Net_Server::create('fork', $address, $port);

$handler = &new Net_Server_Handler_Talkback;
    
// hand over the object that handles server events
$server->setCallbackObject($handler);
$server->setDebugMode(FALSE);
// start the server
$server->start();
?>